from tetra import *
from tetragen import *
from copy import *

class AnyVar:
    def __init__(self):
        self._list = {}

    def __getitem__(self, idx):
        if not idx in self._list.keys():
            self._list[idx] = AnyVar()
        return self._list[idx]

    def __setitem__(self, idx, value):
        self._list[idx] = value

    def __str__(self):
        res = []
        for i, l in self._list.items():
            res.append('%s (%s)' % (i, str(l)))
        if res:
            return '\n'.join(res)
        return 'Empty'
            

class VM:
    def __init__(self, silent = False, screenOut = True):
        self._pmap = {}
        self._varlist = {}
        self._silent = silent
        self._screenOut = screenOut
        self._res = []
        print 'VM initialized'

    def _print(self, arg):
        if self._screenOut:
            print arg
        self._res.append(arg)

    def __getitem__(self, key):
        key = str(key)
        if key in self._pmap.keys():
            n, idx = self._pmap[key]
            return self[n][idx]
        else:
            return self._varlist[key]

    def __setitem__(self, key, value):
        key = str(key)
        if key in self._pmap.keys():
            n, idx = self._pmap[key]
            self[n][idx] = value
        else:
            self._varlist[key] = value

    def __setRes(self, tetrada, value):
        self[tetrada.result] = value
        

    def __getArgValue(self, arg):
        if arg.__class__ == Const:
            return arg.value
        elif arg.__class__ == Var:
            return self[arg]
        elif arg.__class__ == Label:
            return arg.name
        elif arg.__class__ == Func:
            return arg.labelName
        else:
            assert(False)

    def __getArg1(self, tetrada):
        return self.__getArgValue(tetrada.arg1)

    def __getArg2(self, tetrada):
        return self.__getArgValue(tetrada.arg2)

    def __gotoLabel(self, label):
        for i in range(len(self._prog)):
            if self._prog[i].operation == 'DEFL' and self.__getArg1(self._prog[i]) == label:
                self._idx[0] = i
                return
        raise RuntimeError('Label not found: %s' % label)

    def _processCommand(self, t):
#        print '%s:' % self._idx[0], t
        if not self._silent:
            print '%s:' % self._idx[0], t
#        else:
#            print self._idx[0],

#        if 'v4' in self._varlist.keys():
#            print self._varlist['v4']

        if t.operation == 'VARDECL':
            self._varlist[str(t.arg1)] = AnyVar()
        elif t.operation == '+':
            self.__setRes(t, self.__getArg1(t) + self.__getArg2(t))
        elif t.operation == '-':
            self.__setRes(t, self.__getArg1(t) - self.__getArg2(t))
        elif t.operation == '*':
            self.__setRes(t, self.__getArg1(t) * self.__getArg2(t))
        elif t.operation == '/':
            self.__setRes(t, self.__getArg1(t) / self.__getArg2(t))
        elif t.operation == ':=':
            self.__setRes(t, deepcopy(self.__getArg1(t)))
        elif t.operation == 'A2I':
            self.__setRes(t, int(self.__getArg1(t)))
        elif t.operation == 'I2A':
            self.__setRes(t, float(self.__getArg1(t)))
        elif t.operation == 'READ':
            tval = input()
            val = None
            try:
                val = int(tval)
            except:
                val = float(tval)
            self.__setRes(t, val)
        elif t.operation == 'PRINT':
            self._print(self.__getArg1(t))

        elif t.operation == 'DEFL': # define label
            pass
        elif t.operation == 'BRL' : # goto arg1
            self.__gotoLabel(self.__getArg1(t))
        elif t.operation == 'BF'  : # if (arg2) goto arg1
            if self.__getArg2(t) != 0:
                self.__gotoLabel(self.__getArg1(t))
        elif t.operation == 'BZL' : # if (arg2 == 0) goto arg1
            if self.__getArg2(t) == 0:
                self.__gotoLabel(self.__getArg1(t))
        elif t.operation == 'BPL':  # if (arg2 > 0) goto arg1
            if self.__getArg2(t) > 0:
                self.__gotoLabel(self.__getArg1(t))
        elif t.operation == 'BML':  # if (arg2 < 0) goto arg1
            if self.__getArg2(t) < 0:
                self.__gotoLabel(self.__getArg1(t))
        elif t.operation == 'CALL' :
            self._idx = [0] + self._idx
            self.__gotoLabel(self.__getArg1(t))
        elif t.operation == 'RET' :
            self._idx = self._idx[1:]
        elif t.operation == 'SUBS':
            idx = self.__getArg2(t)
            if not str(t.arg1) in self._varlist and not str(t.arg1) in self._pmap.keys():
                raise RuntimeError('Cant find variable: %s' % str(t.arg1))
            self._pmap[str(t.result)] = str(t.arg1), idx
        else:
            raise RuntimeError('Unknown command: %s' % str(t))

    def run(self, prog):
        print
        print 'Listing'
        for i, p in enumerate(prog):
            print '%s:' %i, p
        print
        print 'Running'

        self._idx = [0]
        self._prog = prog
        while self._idx[0] < len(self._prog):
            self._processCommand(self._prog[self._idx[0]])
            self._idx[0] = self._idx[0] + 1
        return self._res
